#ifndef __WELOG_LOG_H__
#define __WELOG_LOG_H__

#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>

#define MAX_LOG_LENGTH (1024 * 1024)
#define NORMAL_LOG_LENGTH (924)

struct message {
    char file_name[128];
    char log_context[1024];
};

#ifndef WELOG_LOG_TAG
#define WELOG_LOG_TAG "WELOG"
#endif

#ifndef WELOG_DEBUG_LOG_TAG
#ifdef ENABLE_WELOG_DEBUG
#define WELOG_DEBUG_LOG_TAG 1
#else
#define WELOG_DEBUG_LOG_TAG 0
#endif /* end of ENABLE_WELOG_DEBUG */
#endif /* end of WELOG_DEBUG_LOG_TAG */

#ifndef LOG_TIME_ADAPT
#define LOG_TIME_ADAPT 1
#endif /* end of LOG_TIME_ADAPT */

#define __FILENAME__ (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__)

#define MESSAGE_HANDLE _IOW('d', 0x02, struct message)
#define LARGE_MESSAGE_HANDLE _IOW('d', 0x03, int)

#ifdef ENABLE_WELOG
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif /* __cplusplus */
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function"

static void stringtolower(char *dest, const char *src)
{
    for (; *src != '\0'; src++) {
        if (*src >= 'A' && *src <= 'Z') {
            *dest++ = *src + ('a' - 'A');
        } else {
            *dest++ = *src;
        }
    }
}

static void file_add_suffix(char *file, const char *suffix)
{
    strcat(file, suffix);
}

static int large_context(char *context)
{
    int ret;
    umask(0);
    int fd = open("/dev/welog", O_RDWR);
    if (fd < 0) {
        printf("open /dev/welog faile\n");
        return -1;
    }
    ret = ioctl(fd, LARGE_MESSAGE_HANDLE, context);
    if (ret) {
        printf("ioctl /dev/welog failed, ret =%d\n", ret);
        return -2;
    }
    close(fd);
    return 0;
}

static int ohos_printf(const char *fmt, ...)
{
    int ret;
    struct message msg = {
        .file_name = "/data/log/welog/",
    };
    char *buff = (char *)malloc(MAX_LOG_LENGTH);
    memset(buff, 0, MAX_LOG_LENGTH);
    char file[128] = {0};
#if LOG_TIME_ADAPT
    struct timeval tv = {0};
    struct tm *tp = NULL;
#endif

    va_list ap;
    va_start(ap, fmt);
    stringtolower(file, WELOG_LOG_TAG);
    file_add_suffix(file, ".log");
    strcat(msg.file_name, file);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wformat-nonliteral"
    ret = vsnprintf(buff, MAX_LOG_LENGTH - 1, fmt, ap);
#pragma clang diagnostic pop
    va_end(ap);
    if (strlen(buff) > NORMAL_LOG_LENGTH) {
        large_context(buff);
        free(buff);
        return 0;
    }
#if LOG_TIME_ADAPT
    gettimeofday(&tv, NULL);
    tp = localtime(&tv.tv_sec);
    snprintf(msg.log_context, sizeof(msg.log_context) - 1, "[%02d:%02d.%03ld]%s", tp->tm_min, tp->tm_sec, tv.tv_usec / 1000, buff);
#else
    snprintf(msg.log_context, sizeof(msg.log_context) - 1, "%s", buff);
#endif
    free(buff);
    umask(0);
    int fd = open("/dev/welog", O_RDWR);
    if (fd < 0) {
        printf("open /dev/welog faile\n");
        return -1;
    }
    ret = ioctl(fd, MESSAGE_HANDLE, &msg);
    if (ret) {
        return -2;
    }
    close(fd);
    return ret;
}
#pragma clang diagnostic pop

#define welog_debug(fmt, ...) do { \
    if (WELOG_DEBUG_LOG_TAG) { \
        (void)ohos_printf("[pid:%d][tid:%d][D][%s][%s:%s:%d]: " fmt "\n", getpid(), gettid(), WELOG_LOG_TAG, __FILENAME__, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
        (void)printf("[pid:%d][tid:%d][D][%s][%s:%s:%d]: " fmt "\n", getpid(), gettid(), WELOG_LOG_TAG, __FILENAME__, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
    } \
} while (0)

#define welog_info(fmt, ...) do { \
    (void)ohos_printf("[pid:%d][tid:%d][I][%s][%s:%s:%d]: " fmt "\n", getpid(), gettid(), WELOG_LOG_TAG, __FILENAME__, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
    (void)printf("[pid:%d][tid:%d][I][%s][%s:%s:%d]: " fmt "\n", getpid(), gettid(), WELOG_LOG_TAG, __FILENAME__, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
} while (0)

#define welog_err(fmt, ...) do { \
    (void)ohos_printf("[pid:%d][tid:%d][E][%s][%s:%s:%d]: " fmt "\n", getpid(), gettid(), WELOG_LOG_TAG, __FILENAME__, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
    (void)printf("[pid:%d][tid:%d][E][%s][%s:%s:%d]: " fmt "\n", getpid(), gettid(), WELOG_LOG_TAG, __FILENAME__, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
} while (0)

#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* __cplusplus */

#else
#define welog_debug(fmt, ...)
#define welog_info(fmt, ...)
#define welog_err(fmt, ...)
#endif /* end of ENABLE_WELOG */
#endif /* __WELOG_LOG_H__ */
